import tkinter as tk
import tensorflow as tf
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# 载入 MNIST 数据集
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data(path='E://py/aipy/3/4/mnist/mnist.npz')
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5  # 归一化到 [-1, 1] 范围

# 定义生成器模型
def create_generator():
    model = tf.keras.Sequential([
        tf.keras.layers.Dense(7*7*256, use_bias=False, input_shape=(100,)),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.LeakyReLU(),
        tf.keras.layers.Reshape((7, 7, 256)),
        tf.keras.layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.LeakyReLU(),
        tf.keras.layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.LeakyReLU(),
        tf.keras.layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh')
    ])
    return model

# 定义判别器模型
def create_discriminator():
    model = tf.keras.Sequential([
        tf.keras.layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1]),
        tf.keras.layers.LeakyReLU(),
        tf.keras.layers.Dropout(0.3),
        tf.keras.layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'),
        tf.keras.layers.LeakyReLU(),
        tf.keras.layers.Dropout(0.3),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(1)
    ])
    return model

# 定义损失函数和优化器
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

def discriminator_loss(real_output, fake_output):
    real_loss = cross_entropy(tf.ones_like(real_output), real_output)
    fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    return total_loss

def generator_loss(fake_output):
    return cross_entropy(tf.ones_like(fake_output), fake_output)

generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)

# 定义训练步骤
def train_step(images):
    noise = tf.random.normal([BATCH_SIZE, 100])

    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
        generated_images = generator(noise, training=True)

        real_output = discriminator(images, training=True)
        fake_output = discriminator(generated_images, training=True)

        gen_loss = generator_loss(fake_output)
        disc_loss = discriminator_loss(real_output, fake_output)

    gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
    gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)

    generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
    discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))

# 定义训练函数
def train(dataset, epochs):
    for epoch in range(epochs):
        for image_batch in dataset:
            train_step(image_batch)

        # 每 10 个 epoch 生成并显示一张图像
        if (epoch + 1) % 10 == 0:
            generate_and_display_images(generator, epoch + 1)

# 生成并显示图像
def generate_and_display_images(model, epoch):
    test_input = tf.random.normal([1, 100])
    predictions = model(test_input, training=False)
    generated_image = predictions[0, :, :, 0]

    plt.imshow(generated_image * 127.5 + 127.5, cmap='gray')
    plt.axis('off')
    plt.title(f'Epoch {epoch}')

    # 在 tkinter 界面中显示图像
    fig = plt.gcf()
    canvas = FigureCanvasTkAgg(fig, master=root)
    canvas.draw()
    canvas.get_tk_widget().grid(row=1, column=0)

# 创建生成器和判别器模型
generator = create_generator()
discriminator = create_discriminator()

# 超参数
BATCH_SIZE = 256
EPOCHS = 50
MAX_SIZE  = 60000

# 创建数据集
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(MAX_SIZE).batch(BATCH_SIZE)

# 创建 tkinter 窗口
root = tk.Tk()
root.title("生成式对抗网络")
root.geometry("400x400")

# 创建按钮来启动训练
def start_training():
    train(train_dataset, EPOCHS)

start_button = tk.Button(root, text="开始训练", command=start_training)
start_button.grid(row=0, column=0)

# 运行 tkinter 主循环
root.mainloop()